petitcomputerfandomcom-20200214-history
Learning Petit Lesson 2b: Loops
This lesson picks up where the last lesson left off, to cover one more important method of controlling the flow of your code: the FOR loop. We will cover how to use a FOR loop, and finally go over some more notes about IF. FOR loops We've seen how we can make a basic infinite loop with GOTO. Many times, however, you'll need a loop that runs a certain number of times and then stop. What if we want to print the numbers from 0 to 5? This is certainly possible with GOTO, and you may have already figured it out on your own: :X = 0 :@LOOP :IF X > 5 THEN GOTO @ENDLOOP :PRINT X :X = X+1 :GOTO @LOOP :@ENDLOOP While this works, it is a bit clumsy. Now let's introduce the FOR loop: :FOR X = 0 to 5 :PRINT X :NEXT This accomplishes exactly the same thing as the GOTO loop, but with 3 lines instead of 7. FOR lets us create a loop that runs a certain number of times. In this case, X is the loop counter - the variable that controls the loop (we can use any valid variable name for the loop counter, but X is a common choice in BASIC). We can set both the starting and ending values of the counter. Each repetition of a loop is known as an iteration. This loop has 6 iterations (0,1,2,3,4,5). If we wanted to print the values from 3 to 6, we would change the previous example to start :FOR X = 3 TO 6 Here we have four iterations. 'STEP' You already understand the basic FOR loop and in many cases that's all you'll need. However, the basic FOR only lets the loop counter go up by 1 each iteration. What if we need to print even numbers from 0 to 6? We could do this: :FOR X = 0 TO 3 :PRINT (X * 2) :NEXT That example is fairly straightforward but things could get messy if we used X repeatedly. Now we introduce the STEP counter: :FOR X = 0 TO 6 STEP 2 :PRINT X :NEXT This prints out the same values as the previous loop. The STEP counter controls how much the loop counter changes at each iteration. The value that the loop counter increases each iteration is called increment; so we can say a standard FOR loop has an increment of 1, but the last example has an increment of 2. Lastly, we will cover looping in reverse: :FOR X = 3 TO 0 STEP -1 :PRINT X :NEXT This loop would print :3 :2 :1 :0 If we need to go through values in reverse order, we can use a negative step value; this is called a decrement, so in this example we are decrementing by 1 each iteration. If you happened to set the STEP to 0, the value of the counter wouldn't change, and the loop would run forever (unless you changed the counter manually inside the loop)! I wouldn't recommend doing so; if you need an infinite loop, GOTO is usually a better approach 'Changing the loop counter' An important point about FOR loops: nothing is stopping you from changing the value of the loop counter in the middle of the loop. You have to be very careful not to accidentally reuse that variable, or your code might do something very strange. Here's a simplified example of what can go wrong: :FOR X = 0 TO 5 :PRINT X :X = X - 1 :NEXT This loop will continue to print 0 forever because we cancel out the loop counter's increment by manually decrementing the counter each iteration. When the loop starts, X is 0. We print 0, then decrement X to -1. When the next iteration starts, X is incremented to 0 again. X never reaches 6, so the loop never stops. Note we can take advantage of this to shortcut our way out of a loop without using a GOTO: :PRINT "The attack hits " NUMTIMESHIT " times!" :FOR X = 1 TO NUMTIMESHIT :PRINT "You took " DAMAGE " damage :HEALTH = HEALTH - DAMAGE :GOSUB @TRYDODGE :IF DODGED THEN X = NUMTIMESHIT + 1 :NEXT In this example, the player has been struck by some attack that hits multiple times. We've decided to annoy the player by printing out how much damage they took for each hit. However, after each hit the player has a chance to dodge the remaining hits, which is calculated at the @TRYDODGE subroutine. If they did dodge successfully, we can break out of the loop early by setting the loop counter higher than its maximum value. Whether you break from a loop using this method or a GOTO is really just a matter of preference in Petit (see notes); despite this example, I would recommend using a GOTO, since the result of a GOTO is more immediately obvious to someone reading your code. Nested FOR loops If you need to repeat an entire loop several times, you can put it in another loop: :FOR X = 0 TO 6 STEP 3 :FOR Y = 0 TO 2 :PRINT (X + Y) :NEXT Y :NEXT X Here the Y loop repeats at each iteration of the X-loop. In this example, X is called an outer loop while Y is an inner loop. This example will print the numbers 0 - 8, each on its own line. Of course this is a silly example and the nested loop isn't really necessary, but it should help you understand the concept. Make sure you read and understand how the code works. We can also use the outer loop variable to control the number of iterations for the inner loop: :FOR X = 0 TO 8 :SUM = 0 :FOR Y = 1 TO X 'do nothing if X is 0 :SUM = SUM + 1 :NEXT Y :PRINT SUM :NEXT X This example also prints the numbers 0 - 8, each on its own line. We reset SUM to 0 at each iteration of the outer loop, and then use the inner loop to increment the SUM. When the inner loop is finished, SUM will be equal to X, so this is an even sillier example. In practice, the primary way you will use nested loops will be with arrays, which are covered in Learning Petit Lesson 4: Arrays. Back to IF Sometimes you want an IF statement to do several things. Unfortunately, Petit's worst feature is that it doesn't let you write a multiline IF statement (see notes). This approach is messy: :IF FOUNDSWORD THEN PRINT "You found the sword!" :IF FOUNDSWORD THEN PLAYERDMG = 10 :IF FOUNDSWORD THEN NUMWEAPONS = NUMWEAPONS + 1 This is technically referred to as very bad code. To prevent you from doing that, Petit lets you combine multiple instructions per line, like this: :IF FOUNDSWORD THEN PRINT "You found the sword!":PLAYERDMG = 10:NUMWEAPONS = NUMWEAPONS + 1 This is technically referred to as very bad code, primarily because it's twice the length of the DS screen. Do any one of these: :IF FOUNDSWORD FALSE THEN GOTO @SKIPGET :PRINT "You found the sword!" :PLAYERDMG = 10 :NUMWEAPONS = NUMWEAPONS + 1 :@SKIPGET or :IF FOUNDSWORD THEN GOSUB @GETSWORD Recommended exercises * Try making 99 Bottles of Beer again using FOR loops instead of GOTO loops Notes * Many new programming languages do not include a GOTO style of command. In these languages, there may be a 'break' (or similar) command for exiting a loop early, but sometimes it's still easier to exit nested loops by changing the loop counter. * Thanks to SquareFingers for corrections and feedback! Category:Kevin's Learning Petit Lessons Category:Resources Category:Tutorials Category:Programming Concepts